package com.samehope.ar.socket;

import com.samehope.ar.service.IDeviceService;
import com.samehope.ar.socket.handler.AppClientHandler;
import com.samehope.ar.socket.handler.HeartBeatHandler;
import com.samehope.ar.socket.handler.HttpRequestHandler;
import com.samehope.ar.util.JwtTokenUtil;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.ResourceLeakDetector;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * @Description: WebSocket服务端
 * @Author: ZhangLuo
 * @Email: 1946430@qq.com
 */
@Slf4j
@Component
public class NettyServer {

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private IDeviceService deviceService;

    private final EventLoopGroup bossGroup = new NioEventLoopGroup();
    private final EventLoopGroup workerGroup = new NioEventLoopGroup();

    private Channel channel;

    public ChannelFuture start(int port) {

        ChannelFuture future = null;

        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup) // 设置主从线程组
                    .channel(NioServerSocketChannel.class)
                    // 服务端可连接队列数, 对应TCP/IP协议listen函数中的backlog参数
                    .option(ChannelOption.SO_BACKLOG, 128)
                    // 立即写出
                    .childOption(ChannelOption.TCP_NODELAY, true)
                    // 保持心跳
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    // 子处理器, 处理workerGroup
                    .childHandler(new ChannelInitializer<SocketChannel>() {

                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            ChannelPipeline pipeline = socketChannel.pipeline();

                            // netty Http编解码助手类
                            pipeline.addLast(new HttpServerCodec());
                            // 写大数据流的支持
                            pipeline.addLast(new ChunkedWriteHandler());
                            // 对HttpMessage进行聚合, 聚合成FullHttpRequest 或 FullHttpResponse
                            pipeline.addLast(new HttpObjectAggregator(1024 * 64));
                            // 授权处理
                            pipeline.addLast(new HttpRequestHandler(jwtTokenUtil, deviceService));
                            // 心跳支持
                            pipeline.addLast(new IdleStateHandler(10, 10, 10));
                            pipeline.addLast(new HeartBeatHandler());

                            // 支持httpWebSocket
                            pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));

                            // 主要事件处理
                            pipeline.addLast(new AppClientHandler(jwtTokenUtil));
                        }
                    });
            //  内存泄漏检测, 开发推荐PARANOID, 线上推荐SIMPLE
            ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.SIMPLE);

            future = bootstrap.bind(port).sync();
            channel = future.channel();
            log.info("┌----------------------------------------------------┐");
            log.info("|Netty Server started on port: {}...               |", port);
            log.info("|Netty Server webSocket connect url: ws://ip:{}/ws |", port);
            log.info("└----------------------------------------------------┘");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (future != null && future.isSuccess()) {
                log.info("Netty server listening on port " + port + " and ready for connections...");
            } else {
                log.error("Netty server start up Error!");
            }
        }

        return future;
    }

    /**
     * 停止服务
     */
    public void destroy() {
        log.info("Shutdown Netty Server...");
        if(channel != null) { channel.close();}
        workerGroup.shutdownGracefully();
        bossGroup.shutdownGracefully();
        log.info("Shutdown Netty Server Success!");
    }

}
